#pragma warning(disable : 4244)
#include <fstream>
#include <iostream>
#pragma warning(disable : 4003)
#include <libjson.h>
#pragma warning(default : 4003)
#include "Avalon/IO/ServerConnection.hpp"
#include "Avalon/ServiceLocator/ServiceChannelFactory.hpp"
#include "Avalon/ServiceLocator/ServiceLocatorClient.hpp"
#include "Avalon/UidService/MySqlUidDataStore.hpp"
#include "Avalon/UidService/UidServer.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Network;
using namespace Avalon::Serialization;
using namespace Avalon::ServiceLocator;
using namespace Avalon::UidService;
using namespace boost;
using namespace boost::posix_time;
using namespace libjson;
using namespace std;

int main(int argc, const char** argv) {

  // Load the config file.
  JSONNode config;
  ifstream source("config.json");
  if(!source.good()) {
    cout << "config.json not found." << endl;
    return -1;
  }
  string configContents = string(istreambuf_iterator<char>(source),
    istreambuf_iterator<char>());
  if(!is_valid(configContents)) {
    cout << "JSON parsing error in config.json" << endl;
    return -1;
  }
  config = parse(configContents);

  // Connect to the service locator.
  Channel* serviceClientChannel;
  DataShuttleFactory* serviceClientDataShuttle;
  int timeout;
  string username;
  string password;
  try {
    ServiceChannelFactory channelFactory;
    JSONNode& serviceClientConfig = config["service_client"];
    serviceClientChannel = channelFactory.CreateChannel(
      serviceClientConfig["channel"].write());
    serviceClientDataShuttle = channelFactory.CreateDataShuttleFactory(
      serviceClientConfig["data_shuttle"].write());
    timeout = serviceClientConfig["timeout"].as_int();
    username = serviceClientConfig["username"].as_string();
    password = serviceClientConfig["password"].as_string();
  } catch(std::exception& e) {
    cout << e.what() << endl;
    return -1;
  }
  ServiceLocatorClient serviceClient(serviceClientChannel,
    serviceClientDataShuttle, seconds(timeout));
  cout << "Connecting to service locator: ";
  try {
    serviceClient.Login(username, password);
  } catch(std::exception& e) {
    cout << e.what() << endl;
    return -1;
  }
  cout << "Passed." << endl;

  // Launch the UID service.
  MySqlUidDataStore* dataStore;
  try {
    const JSONNode& dataStoreConfig = config["data_store"];
    string hostname = dataStoreConfig["hostname"].as_string();
    unsigned short port = static_cast<unsigned short>(
      dataStoreConfig["port"].as_int());
    string username = dataStoreConfig["username"].as_string();
    string password = dataStoreConfig["password"].as_string();
    string schema = dataStoreConfig["schema"].as_string();
    dataStore = new MySqlUidDataStore(IpAddress(hostname, port), schema,
      username, password);
  } catch(std::exception& e) {
    cout << e.what() << endl;
    return -1;
  }
  ServerConnection* serverConnection;
  DataShuttleFactory* serverDataShuttle;
  int serverTimeout;
  try {
    ServiceChannelFactory channelFactory;
    JSONNode& serverConfig = config["server"];
    serverConnection = static_cast<ServerConnection*>(
      channelFactory.CreateConnection(serverConfig["connection"].write()));
    serverDataShuttle = channelFactory.CreateDataShuttleFactory(
      serverConfig["data_shuttle"].write());
    serverTimeout = serverConfig["timeout"].as_int();
  } catch(std::exception& e) {
    cout << e.what() << endl;
    return -1;
  }
  UidServer uidServer(&serviceClient, dataStore, serverConnection,
    serverDataShuttle, seconds(serverTimeout));
  cout << "Starting UID server: ";
  try {
    uidServer.Start();
  } catch(std::exception& e) {
    cout << e.what() << endl;
    return -1;
  }
  cout << "Passed." << endl;

  // Register the service.
  cout << "Registering service: ";
  try {
    serviceClient.Register(config["service"]["name"].as_string(),
      config["service"].write());
  } catch(std::exception& e) {
    cout << e.what() << endl;
    return -1;
  }
  cout << "Passed." << endl;
  while(uidServer.IsRunning() && !ReceivedKillEvent()) {
    boost::this_thread::sleep(seconds(1));
  }
  cout << "Shutting down." << endl;
  uidServer.Stop();
  serviceClient.Close();
  return 0;
}
